#include "ShaderManagerGLES.h"
#include "ShaderProgramGLES.h"
#include "RendererGLES.h"

namespace Nezha
{

	const _string g_GLESDefaultShaderVS[DST_NUM] =
	{
		// vertex color
		"uniform mat4 WVP_MATRIX;"
		"uniform vec4 u_color;"
		"attribute vec3 a_position;"
		"varying vec4 v_color;"
		"void main(){v_color=u_color;"
		"gl_Position=WVP_MATRIX*vec4(a_position, 1.0);}",
		//"gl_Position=vec4(a_position, 1.0)*WVP_MATRIX;}",
		//"gl_Position=vec4(a_position, 1.0);}",

		// single texture mapping
		"uniform mat4 WVP_MATRIX;"
		"attribute vec3 a_position;"
		"attribute vec2 a_texcoord;"
		"varying vec2 v_texcoord;"
		"void main(){"
		"gl_Position=WVP_MATRIX*vec4(a_position, 1.0);"
		"v_texcoord=a_texcoord;"
		"}",

		// diffuse
		"uniform mat4 WVP_MATRIX;"
		"attribute vec3 a_position;"
		"attribute vec3 a_normal;"
		"varying vec3 v_pos;"
		"varying vec3 v_normal;"
		"void main(){"
		"gl_Position=WVP_MATRIX*vec4(a_position, 1.0);"
		"v_pos=a_position;"
		"v_normal=a_normal;"
		"}",

		// specular
		"uniform mat4 WVP_MATRIX;"
		"attribute vec3 a_position;"
		"attribute vec3 a_normal;"
		"attribute vec2 a_texcoord;"
		"varying vec3 v_pos;"
		"varying vec3 v_normal;"
		"varying vec2 v_texcoord;"
		"void main(){"
		"gl_Position=WVP_MATRIX*vec4(a_position, 1.0);"
		"v_pos=a_position;"
		"v_normal=a_normal;"
		"v_texcoord=a_texcoord;"
		"}",

		// depth packed
		"uniform mat4 WVP_MATRIX;"
		"attribute vec3 a_position;"
		"varying float v_depth;"
		"void main(){"
		"gl_Position=WVP_MATRIX*vec4(a_position, 1.0);"
		"v_depth=gl_Position.z / gl_Position.w;}",
	};

	const _string g_GLESDefaultShaderPS[DST_NUM] =
	{
		// vertex color
		"precision mediump float;"
		"varying vec4 v_color;"
		"void main(){gl_FragColor=v_color;}",

		// single texture mapping.
		// FIXME caculate attenuation.
		"precision mediump float;"
		//"precision highp float;"
		"uniform sampler2D Tex;"
		"varying vec2 v_texcoord;"
		"void main(){"
		"gl_FragColor=texture2D(Tex, v_texcoord);"
		"}",

		// diffuse
		// FIXME caculate attenuation.
		"precision mediump float;"
		"uniform vec3 CAM_LOCAL_POS;"
		"uniform vec3 LIT0_LOCAL_DIR;"
		"uniform vec4 LIT0_COLOR;"
		//"uniform vec4 LIT0_ATTENUATION;"
		"uniform vec4 MatDiffuseColor;"
		"varying vec3 v_pos;"
		"varying vec3 v_normal;"
		"void main(){"
		"vec3 normal=normalize(v_normal);"
		"float NDotL=-dot(normal, LIT0_LOCAL_DIR);"
		"vec3 viewVec=normalize(CAM_LOCAL_POS-v_pos);"
		"vec4 diffCol=MatDiffuseColor * LIT0_COLOR * max(0.0, NDotL) * 2.0;"
		"gl_FragColor=diffCol;"
		"}",

		// specular
		// FIXME caculate attenuation.
		"precision mediump float;"
		//"precision highp float;"
		"uniform vec3 CAM_LOCAL_POS;"
		"uniform vec3 LIT0_LOCAL_DIR;"
		"uniform vec4 LIT0_COLOR;"
		//"uniform vec4 LIT0_ATTENUATION;"
		"uniform vec4 MatSpecColor;"
		"uniform vec4 MatDiffuseColor;"
		"uniform sampler2D Tex;"
		"varying vec3 v_pos;"
		"varying vec3 v_normal;"
		"varying vec2 v_texcoord;"
		"void main(){"
		"vec3 normal=normalize(v_normal);"
		"float NDotL=-dot(normal, LIT0_LOCAL_DIR);"
		"vec3 viewVec=normalize(CAM_LOCAL_POS-v_pos);"
		"vec3 halfVec=normalize(viewVec-LIT0_LOCAL_DIR);"
		"vec4 diffCol=MatDiffuseColor * LIT0_COLOR * max(0.0, NDotL) * 2.0;"
		"diffCol=diffCol*texture2D(Tex, v_texcoord);"
		"vec4 specCol=MatSpecColor * LIT0_COLOR * pow( max(0.0, dot(normal, halfVec)), 128.0 );"
		"gl_FragColor=diffCol + specCol;"
		//"gl_FragColor=diffCol;"
		"}",

		// depth packed
		"precision mediump float;"
		"varying float v_depth;"
		"void main(){"
		"gl_FragColor.r = v_depth;"
		"gl_FragColor.g = v_depth * 256;"
		"gl_FragColor.b = v_depth * 65536;"
		"gl_FragColor.a = v_depth * 16777216"
		"}",
	};


	ShaderManagerGLES::ShaderManagerGLES()
	{

	}

	ShaderManagerGLES::~ShaderManagerGLES()
	{

	}

	ShaderProgramPtr ShaderManagerGLES::findShader(const _string& name)
	{
		ProgramNameMap::iterator it = mPrograms.find(name);
		if(it != mPrograms.end())
		{
			return it->second;
		}

		ShaderProgramPtr ptr;
		return ptr;
	}

	ShaderProgramPtr ShaderManagerGLES::createShader(const _string& source, ShaderProgram::Language lang)
	{
		// TODO

		ShaderProgramPtr ptr;
		return ptr;
	}

	ShaderProgramPtr ShaderManagerGLES::createShader(DefaultShaderType dst)
	{
		ShaderProgramPtr ptr = findShader(ShaderProgram::msDefaultShaderName[dst]);

		if(ptr.notNull())
		{
			return ptr;
		}

		RendererGLES* r = static_cast<RendererGLES*>(Renderer::GetGlobal());
		ProgramGLES* pg = NZ_New ProgramGLES(r);

		bool shaderValid = false;

		switch(dst)
		{
		case DST_VERTEX_COLOR:
		case DST_DIRECTIONAL_DIFFUSE:
		case DST_DIRECTIONAL_SPECULAR:
			if(pg->loadFromSource(g_GLESDefaultShaderVS[dst], ShaderProgram::VERTEX_SHADER))
			{
				if(pg->loadFromSource(g_GLESDefaultShaderPS[dst], ShaderProgram::PIXEL_SHADER))
				{
					shaderValid = true;
				}
			}
			break;
		}

		if(!shaderValid)
		{
			NZ_Delete pg;
		}
		else
		{
			pg->link();

			ptr = pg;
			mPrograms.insert(MapPair<_string, ShaderProgramPtr>(ShaderProgram::msDefaultShaderName[dst], ptr));
		}

		return ptr;
	}

	void ShaderManagerGLES::_garbageDispose()
	{
		for(ProgramNameMap::iterator it = mPrograms.begin(); it != mPrograms.end(); )
		{
			if(it->second->getRefCount() == 1)
			{
				it->second = NULL;
				mPrograms.erase(++it);
			}
			else
			{
				it++;
			}
		}
	}

	void ShaderManagerGLES::removeAll()
	{
		for(ProgramNameMap::iterator it = mPrograms.begin(); it != mPrograms.end(); it++)
		{
			it->second.release();
		}

		mPrograms.clear();
	}

}//end namespace Nezha